import os
import aiofiles
import hashlib
from typing import Optional, BinaryIO
from pathlib import Path

from config.video_config import VIDEO_CONFIG
from config.logging_config import ProjectLoggers

logger = ProjectLoggers.get_service_logger()

class FileHelper:
    """文件处理工具类"""
    
    @staticmethod
    def validate_file_size(file_size: int) -> bool:
        """验证文件大小"""
        return file_size <= VIDEO_CONFIG['max_file_size']
    
    @staticmethod
    def get_file_extension(filename: str) -> str:
        """获取文件扩展名"""
        return Path(filename).suffix.lower().lstrip('.')
    
    @staticmethod
    def get_file_basename(filename: str) -> str:
        """获取文件基本名称（不含扩展名）"""
        return Path(filename).stem
    
    @staticmethod
    def generate_unique_filename(original_filename: str, prefix: str = None) -> str:
        """生成唯一文件名"""
        import uuid
        import time
        
        file_ext = FileHelper.get_file_extension(original_filename)
        base_name = FileHelper.get_file_basename(original_filename)
        
        # 生成唯一标识
        unique_id = str(uuid.uuid4())[:8]
        timestamp = str(int(time.time()))
        
        if prefix:
            new_filename = f"{prefix}_{base_name}_{timestamp}_{unique_id}.{file_ext}"
        else:
            new_filename = f"{base_name}_{timestamp}_{unique_id}.{file_ext}"
        
        return new_filename
    
    @staticmethod
    async def save_uploaded_file(file_content: bytes, filename: str) -> str:
        """保存上传的文件到临时目录"""
        try:
            # 生成唯一文件名
            unique_filename = FileHelper.generate_unique_filename(filename, "upload")
            temp_filepath = os.path.join(VIDEO_CONFIG['temp_dir'], unique_filename)
            
            # 异步写入文件
            async with aiofiles.open(temp_filepath, 'wb') as f:
                await f.write(file_content)
            
            logger.info(f"文件保存成功: {temp_filepath}")
            return temp_filepath
            
        except Exception as e:
            logger.error(f"文件保存失败 {filename}: {str(e)}")
            raise
    
    @staticmethod
    async def read_file_chunks(file_path: str, chunk_size: int = 8192):
        """分块读取文件内容"""
        try:
            async with aiofiles.open(file_path, 'rb') as f:
                while True:
                    chunk = await f.read(chunk_size)
                    if not chunk:
                        break
                    yield chunk
        except Exception as e:
            logger.error(f"读取文件失败 {file_path}: {str(e)}")
            raise
    
    @staticmethod
    async def calculate_file_hash(file_path: str) -> str:
        """计算文件MD5哈希值"""
        try:
            hash_md5 = hashlib.md5()
            async with aiofiles.open(file_path, 'rb') as f:
                async for chunk in FileHelper.read_file_chunks(file_path):
                    hash_md5.update(chunk)
            
            file_hash = hash_md5.hexdigest()
            logger.info(f"文件哈希计算完成: {file_path} -> {file_hash}")
            return file_hash
            
        except Exception as e:
            logger.error(f"计算文件哈希失败 {file_path}: {str(e)}")
            raise
    
    @staticmethod
    def get_file_size(file_path: str) -> int:
        """获取文件大小"""
        try:
            return os.path.getsize(file_path)
        except Exception as e:
            logger.error(f"获取文件大小失败 {file_path}: {str(e)}")
            return 0
    
    @staticmethod
    def ensure_directory_exists(directory: str) -> bool:
        """确保目录存在"""
        try:
            os.makedirs(directory, exist_ok=True)
            return True
        except Exception as e:
            logger.error(f"创建目录失败 {directory}: {str(e)}")
            return False
    
    @staticmethod
    def delete_file(file_path: str) -> bool:
        """删除文件"""
        try:
            if os.path.exists(file_path):
                os.remove(file_path)
                logger.info(f"文件删除成功: {file_path}")
            return True
        except Exception as e:
            logger.error(f"文件删除失败 {file_path}: {str(e)}")
            return False
    
    @staticmethod
    def get_safe_filename(filename: str) -> str:
        """获取安全的文件名（移除特殊字符）"""
        import re
        
        # 保留文件扩展名
        name_part = FileHelper.get_file_basename(filename)
        ext_part = FileHelper.get_file_extension(filename)
        
        # 移除特殊字符，只保留字母、数字、下划线、连字符
        safe_name = re.sub(r'[^\w\-_\u4e00-\u9fa5]', '_', name_part)
        
        return f"{safe_name}.{ext_part}" if ext_part else safe_name
    
    @staticmethod
    def format_file_size(size_bytes: int) -> str:
        """格式化文件大小显示"""
        if size_bytes == 0:
            return "0B"
        
        size_names = ["B", "KB", "MB", "GB", "TB"]
        i = 0
        while size_bytes >= 1024 and i < len(size_names) - 1:
            size_bytes /= 1024
            i += 1
        
        return f"{size_bytes:.2f}{size_names[i]}"
    
    @staticmethod
    async def copy_file(src_path: str, dst_path: str) -> bool:
        """异步复制文件"""
        try:
            async with aiofiles.open(src_path, 'rb') as src:
                async with aiofiles.open(dst_path, 'wb') as dst:
                    async for chunk in FileHelper.read_file_chunks(src_path):
                        await dst.write(chunk)
            
            logger.info(f"文件复制成功: {src_path} -> {dst_path}")
            return True
            
        except Exception as e:
            logger.error(f"文件复制失败 {src_path} -> {dst_path}: {str(e)}")
            return False
    
    @staticmethod
    def cleanup_temp_directory() -> int:
        """清理临时目录中的旧文件"""
        try:
            import time
            
            temp_dir = VIDEO_CONFIG['temp_dir']
            if not os.path.exists(temp_dir):
                return 0
            
            current_time = time.time()
            cleanup_threshold = 3600  # 1小时前的文件
            cleaned_count = 0
            
            for filename in os.listdir(temp_dir):
                file_path = os.path.join(temp_dir, filename)
                if os.path.isfile(file_path):
                    file_age = current_time - os.path.getmtime(file_path)
                    if file_age > cleanup_threshold:
                        if FileHelper.delete_file(file_path):
                            cleaned_count += 1
            
            logger.info(f"临时目录清理完成，清理了 {cleaned_count} 个文件")
            return cleaned_count
            
        except Exception as e:
            logger.error(f"清理临时目录失败: {str(e)}")
            return 0 